#include "hal_spi_nand_driver.h"
#include "hal_nand_device_info.h"
#include "driver/spi_master.h"
#include "driver/gpio.h"
#include "sdkconfig.h"
#include <string.h>
#include <errno.h>

// #define DBG_TAG "NAND_G_DRV"
// #define DBG_LVL DBG_LOG
// #include <rtdbg.h>

#include "esp_log.h"

#define NAND_DRV_LOG_TAG "NAND_G_DRV"

#ifndef CONFIG_LOG_MAXIMUM_LEVEL
#define CONFIG_LOG_MAXIMUM_LEVEL ESP_LOG_DEBUG
#endif

#define LOG_E(format, ...) ESP_LOGE(NAND_DRV_LOG_TAG, format, ##__VA_ARGS__)
#define LOG_W(format, ...) ESP_LOGW(NAND_DRV_LOG_TAG, format, ##__VA_ARGS__)
#define LOG_I(format, ...) ESP_LOGI(NAND_DRV_LOG_TAG, format, ##__VA_ARGS__)
#define LOG_D(format, ...) ESP_LOGD(NAND_DRV_LOG_TAG, format, ##__VA_ARGS__)
#define LOG_V(format, ...) ESP_LOGV(NAND_DRV_LOG_TAG, format, ##__VA_ARGS__)


/*************************************************************************/
/********************* 全局变量 硬件&FLASH本身的操作句柄 *******************/
/*************************************************************************/

HAL_NAND_Device_t hal_nand_device =
{
    .spi_device = NULL,
    .nand_flash_info = &hal_flash_info_table[0],
};


/*************************************************************************/
/**************************** 指令转换系统 ********************************/
/************* 如果通用参数和主控的参数不一致，在此处配置转换 ****************/
/**由于指令解析次数较多，不建议使用for循环进行匹配，可以使用list来提高查找效率**/
/*************************************************************************/

#if 1
//无需转换时使用下列宏控
#define CMD_XFER_MODE(x) x
#define ADDR_XFER_MODE(x) x
#define DATA_XFER_MODE(x) x
#define ADDR_SIZE_MODE(x) x
#else
const uint32_t CMD_XFER_MODE_LIST[]=
{
    HAL_OSPI_INSTRUCTION_NONE,
    HAL_OSPI_INSTRUCTION_1_LINE,
    HAL_OSPI_INSTRUCTION_2_LINES,
    HAL_OSPI_INSTRUCTION_4_LINES,
    HAL_OSPI_INSTRUCTION_8_LINES
};

#define CMD_XFER_MODE(x) CMD_XFER_MODE_LIST[x]

const uint32_t ADDR_XFER_MODE_LIST[]=
{
    HAL_OSPI_ADDRESS_NONE,
    HAL_OSPI_ADDRESS_1_LINE,
    HAL_OSPI_ADDRESS_2_LINES,
    HAL_OSPI_ADDRESS_4_LINES,
    HAL_OSPI_ADDRESS_8_LINES
};

#define ADDR_XFER_MODE(x) ADDR_XFER_MODE_LIST[x]

const uint32_t DATA_XFER_MODE_LIST[]=
{
    HAL_OSPI_DATA_NONE,
    HAL_OSPI_DATA_1_LINE,
    HAL_OSPI_DATA_2_LINES,
    HAL_OSPI_DATA_4_LINES,
    HAL_OSPI_DATA_8_LINES
};

#define DATA_XFER_MODE(x) DATA_XFER_MODE_LIST[x]

const uint32_t ADDR_SIZE_LIST[]=
{
    0,
    HAL_OSPI_ADDRESS_8_BITS,
    HAL_OSPI_ADDRESS_16_BITS,
    HAL_OSPI_ADDRESS_24_BITS,
    HAL_OSPI_ADDRESS_32_BITS
};

#define ADDR_SIZE_MODE(x) ADDR_SIZE_LIST[x]

#endif

/*************************************************************************/
/*****************************硬件层面的初始化操作**************************/
/**如果FLASH初始化时需要使能硬件，在该处实现，并适配到HAL_NAND_BSP_Init()函数**/
/*************************************************************************/

//ESP32使用QSPI和NAND通信，硬件定义如下
//IO9-D3
//IO10-CS
//IO11-D0
//IO12-CLK
//IO13-D1
//IO14-D2
//查规格书可得使用的为SPI2的主接口,通信模式允许SINGLE/DUAL/QUAD
#define NAND_QSPI_PIN_CS  GPIO_NUM_10
#define NAND_QSPI_PIN_CLK GPIO_NUM_12
#define NAND_QSPI_PIN_IO0 GPIO_NUM_11
#define NAND_QSPI_PIN_IO1 GPIO_NUM_13
#define NAND_QSPI_PIN_IO2 GPIO_NUM_14
#define NAND_QSPI_PIN_IO3 GPIO_NUM_9

#define ESP32_NAND_SPI_HOST SPI2_HOST
//由于客户走线长，CLK压低到5MHz
#define ESP32_NAND_SPI_CLK 5*1000*1000

static void ESP32_SPI_Init(void)
{
    int ret = -1;
    spi_bus_config_t nand_buscfg = {
        .data0_io_num = NAND_QSPI_PIN_IO0,
        .data1_io_num = NAND_QSPI_PIN_IO1,
        .data2_io_num = NAND_QSPI_PIN_IO2,
        .data3_io_num = NAND_QSPI_PIN_IO3,
        .sclk_io_num = NAND_QSPI_PIN_CLK,        
        .max_transfer_sz = 4096,
        .flags = SPICOMMON_BUSFLAG_QUAD,
    };

    ret = spi_bus_initialize(ESP32_NAND_SPI_HOST, &nand_buscfg, SPI_DMA_CH_AUTO);
    if(ret != ESP_OK)
    {
        LOG_E("NAND QSPI host init failed!");
    }

    spi_device_interface_config_t devcfg = {
        .command_bits = 8,
        .clock_speed_hz = ESP32_NAND_SPI_CLK,
        .mode = 0,          //SPI mode 0
        .spics_io_num = NAND_QSPI_PIN_CS,
        .queue_size = 4,
        .input_delay_ns = 0, 
        .flags = SPI_DEVICE_HALFDUPLEX,
    };    
    ret = spi_bus_add_device(ESP32_NAND_SPI_HOST, &devcfg, (spi_device_handle_t *)&hal_nand_device.spi_device);
    if(ret != ESP_OK)
    {
        LOG_E("NAND QSPI bus added failed!");
    }
}

static void HAL_NAND_BSP_Init(void)
{
    ESP32_SPI_Init();
}

/***********************************************************************/
/***************************SPI/QSPI 通信函数***************************/
/*******************根据硬件的SPI/QSPI收发函数进行适配********************/
/***********************************************************************/

/**
 * @brief  HAL_SPI_NAND_xfer SPI/QSPI通信函数
 * @param  nand_device      HAL_NAND_Device_t类型，绑定NAND FLASH设备和SPI通信函数
 * @param  cmd              uint8_t类型，指令本身，函数内部会根据指令自动查找指令参数
 * @param  addr             uint32_t类型，指令地址，在NAND中生效的地址
 * @param  din              uint8_t*类型，输入buffer地址，需要接收数据时传入，否则置为NULL
 * @param  dout             uint8_t*类型，输出buffer地址，需要发送数据时传入，否则置为NULL
 * @param  len              uint32_t类型，指令需要发送或接收的数据长度，不用置0
 * @param  xfer_mode        spi_xfer_mode_e类型，暂时未使用，保留作为控制SPI发送方式的参数（即选择FIFO发送还是DMA发送）
 * @return int 成功为0，失败时可以根据平台特性编制<0的错误码
 */
static int HAL_SPI_NAND_xfer(HAL_NAND_Device_t nand_device, uint8_t cmd, uint32_t addr, uint8_t *din, uint8_t *dout, uint32_t len, spi_xfer_mode_e xfer_mode)
{
    spi_transaction_ext_t trans_param = {0};
    NAND_cmd_t *cmd_list = nand_device.nand_flash_info->cmd_list;
    uint8_t cmd_index = 0;

    for(cmd_index = 0; cmd_index < nand_device.nand_flash_info->cmd_index_max; cmd_index++)
    {
        if(cmd == cmd_list[cmd_index].cmd)
        {
            break;
        }
    }

    if(cmd_index >= nand_device.nand_flash_info->cmd_index_max)
    {
        LOG_E("cmd not found");
        return -1;
    }

    trans_param.base.flags = SPI_TRANS_VARIABLE_CMD | SPI_TRANS_VARIABLE_ADDR | SPI_TRANS_VARIABLE_DUMMY ;

    if(cmd_list[cmd_index].data_xfer_mode == NAND_XFER_DUAL)
    {
        trans_param.base.flags |= SPI_TRANS_MODE_DIO;
        if(cmd_list[cmd_index].data_xfer_mode == cmd_list[cmd_index].addr_xfer_mode)
        {
            trans_param.base.flags |= SPI_TRANS_MULTILINE_ADDR;
        }
    }
    else if(cmd_list[cmd_index].data_xfer_mode == NAND_XFER_QUAD)
    {
        trans_param.base.flags |= SPI_TRANS_MODE_QIO;
        if(cmd_list[cmd_index].data_xfer_mode == cmd_list[cmd_index].addr_xfer_mode)
        {
            trans_param.base.flags |= SPI_TRANS_MULTILINE_ADDR;
        }        
    }


    trans_param.base.cmd = cmd_list[cmd_index].cmd;
    trans_param.base.addr = addr;
    trans_param.base.length = 8 * len;

    if(din != NULL && dout != NULL)
    {
        LOG_E("Don't support din and dout at the same time");
    }

    trans_param.base.rx_buffer = din;
    trans_param.base.tx_buffer = dout;

    trans_param.address_bits = 8 * cmd_list[cmd_index].addr_bytes;
    trans_param.command_bits = 8 * 1;
    trans_param.dummy_bits = 8 * cmd_list[cmd_index].dummy_bytes;
    
    spi_device_polling_transmit((spi_device_handle_t)nand_device.spi_device, &trans_param.base);

    return 0;
}

/******************system tick 适配，和当前系统对接即可*******************/

static inline unsigned long long get_ticks(void)
{
	return (unsigned long long)xTaskGetTickCount();
}

static inline void udelay(unsigned int us)
{
	unsigned int ms = us/1000;
    pdMS_TO_TICKS(ms);
	vTaskDelay(ms);
	return;
}

static inline void mdelay(unsigned int ms)
{
    pdMS_TO_TICKS(ms);
	vTaskDelay(ms);
	return;
}

/*****************以下函数为固定流程，根据硬件情况微调即可******************/

/**
 * @brief HAL_SPI_NAND_Init 初始化硬件，挂载FLASH并且检查ID是否匹配
 * @param  hal_nand_device  HAL_NAND_Device_t类型，绑定NAND FLASH设备和SPI通信函数
 * @return true 成功
 * @return false 失败
 */
bool HAL_SPI_NAND_Init(HAL_NAND_Device_t hal_nand_device)
{
    char nand_jedec_id[3] = {0};
    //uint8_t addr = 0x00;

    HAL_NAND_BSP_Init();

    mdelay(100);
    HAL_SPI_NAND_xfer(hal_nand_device, SPINAND_CMD_RESET, 0, NULL, NULL, 0, SPI_FIFO);
    mdelay(100);

    HAL_SPI_NAND_xfer(hal_nand_device, SPINAND_CMD_READ_ID, 0x00, nand_jedec_id, NULL, 2, SPI_FIFO);

    LOG_D("MFR ID: %02X  DEV ID: %02X", nand_jedec_id[0], nand_jedec_id[1]);

    if(hal_nand_device.nand_flash_info->manufacturer_id != nand_jedec_id[0] ||\
    hal_nand_device.nand_flash_info->device_id != nand_jedec_id[1])
    {
        LOG_E("nand id not match");
        return false;
    }

    return true;
}

/**
 * @brief HAL_SPI_NAND_Read_ID 读取ID
 * @param  hal_nand_device  HAL_NAND_Device_t类型，绑定NAND FLASH设备和SPI通信函数
 * @param  din_id           uint8_t*类型，ID接收地址
 * @return int 通信函数的返回值
 */
int HAL_SPI_NAND_Read_ID(HAL_NAND_Device_t hal_nand_device, uint8_t *din_id)
{
    return HAL_SPI_NAND_xfer(hal_nand_device, SPINAND_CMD_READ_ID, 0x00, din_id, NULL, 2, SPI_FIFO);
}

/**
 * @brief HAL_SPI_NAND_Get_Feature 读取特性寄存器
 * @param  hal_nand_device  NAND设备
 * @param  addr             状态寄存器地址，分别是A0，B0，C0，代表三个八位寄存器
 * @param  din_feature      接收寄存器值的地址，一个uint8_t即可
 * @return int 通信函数的返回值
 */
int HAL_SPI_NAND_Get_Feature(HAL_NAND_Device_t hal_nand_device, uint32_t addr, uint8_t *din_feature)
{

    return HAL_SPI_NAND_xfer(hal_nand_device, SPINAND_CMD_GET_FEATURE, addr, din_feature, NULL, 1, SPI_FIFO);
}

/**
 * @brief HAL_SPI_NAND_Set_Feature 写入特性寄存器
 * @param  hal_nand_device  NAND设备
 * @param  addr             状态寄存器地址，分别是A0，B0，C0，代表三个八位寄存器
 * @param  dout_feature     写入寄存器值的地址，一个uint8_t即可
 * @return int 通信函数的返回值
 */ 
int HAL_SPI_NAND_Set_Feature(HAL_NAND_Device_t hal_nand_device, uint32_t addr, uint8_t *dout_feature)
{

    return HAL_SPI_NAND_xfer(hal_nand_device, SPINAND_CMD_SET_FEATURE, addr, NULL, dout_feature, 1, SPI_FIFO);
}

/**
 * @brief HAL_SPI_NAND_Read_Status 读取状态寄存器，即地址为0xc0的特性寄存器
 * @param  hal_nand_device  NAND设备
 * @param  status           状态寄存器值，一个uint8_t即可
 * @return int 通信函数的返回值
 */
int HAL_SPI_NAND_Read_Status(HAL_NAND_Device_t hal_nand_device, uint8_t *status)
{
    return HAL_SPI_NAND_Get_Feature(hal_nand_device, REG_STATUS, status);
}

/**
 * @brief HAL_SPI_NAND_Get_Cfg 读取配置寄存器，即地址为0xb0的特性寄存器
 * @param  hal_nand_device  NAND设备
 * @param  cfg              配置寄存器的值，一个uint8_t即可
 * @return int 通信函数的返回值
 */
int HAL_SPI_NAND_Get_Cfg(HAL_NAND_Device_t hal_nand_device, uint8_t *cfg)
{
    return HAL_SPI_NAND_Get_Feature(hal_nand_device, REG_CFG, cfg);
}

/**
 * @brief HAL_SPI_NAND_Set_Cfg 设定配置寄存器值，即地址为0xb0的特性寄存器
 * @param  hal_nand_device  NAND设备
 * @param  cfg              配置寄存器的值，一个uint8_t即可
 * @return int 通信函数的返回值
 */
int HAL_SPI_NAND_Set_Cfg(HAL_NAND_Device_t hal_nand_device, uint8_t *cfg)
{
	return HAL_SPI_NAND_Set_Feature(hal_nand_device, REG_CFG, cfg);
}

/**
 * @brief HAL_SPI_NAND_Lock_Block 配置写保护（OTP）寄存器的值，即地址为0xa0的特性寄存器
 * @param  hal_nand_device  NAND设备
 * @param  lock            OTP寄存器的值，一个uint8_t即可，一般直接写为0，全片解锁即可
 * @return int 通信函数的返回值
 */
int HAL_SPI_NAND_Lock_Block(HAL_NAND_Device_t hal_nand_device, uint8_t *lock)
{
	return HAL_SPI_NAND_Set_Feature(hal_nand_device, REG_BLOCK_LOCK, lock);
}

/**
 * @brief HAL_SPI_NAND_Get_Lock_Block 读取写保护（OTP）寄存器的值，即地址为0xa0的特性寄存器
 * @param  hal_nand_device  NAND设备
 * @param  lock            OTP寄存器的值，一个uint8_t即可，上电默认为0x38，即全片锁定
 * @return int 通信函数的返回值
 */
int HAL_SPI_NAND_Get_Lock_Block(HAL_NAND_Device_t hal_nand_device, uint8_t *lock)
{
	return HAL_SPI_NAND_Get_Feature(hal_nand_device, REG_BLOCK_LOCK, lock);
}

/**
 * @brief HAL_SPI_NAND_Enable_Ecc 使能ECC，每写入512byte数据，会自动触发一次ECC校验，结果存在0xc0特性寄存器中
 * @param  hal_nand_device  NAND设备
 * @return int 通信函数的返回值
 */
int HAL_SPI_NAND_Enable_Ecc(HAL_NAND_Device_t hal_nand_device)
{
    uint8_t cfg = 0;

    HAL_SPI_NAND_Get_Cfg(hal_nand_device, &cfg);
    if ((cfg & CFG_ECC_MASK) == CFG_ECC_ENABLE)
    {
		return 0;
    }

    cfg |= CFG_ECC_ENABLE;
    return HAL_SPI_NAND_Set_Cfg(hal_nand_device, &cfg);
}

/**
 * @brief HAL_SPI_NAND_Enable_Ecc 关闭ECC，每写入512byte数据，会自动触发一次ECC校验，结果存在0xc0特性寄存器中
 * @param  hal_nand_device  NAND设备
 * @return int 通信函数的返回值
 */
int HAL_SPI_NAND_Disable_Ecc(HAL_NAND_Device_t hal_nand_device)
{
    uint8_t cfg = 0;

    HAL_SPI_NAND_Get_Cfg(hal_nand_device, &cfg);
	if ((cfg & CFG_ECC_MASK) == CFG_ECC_ENABLE) {
		cfg &= ~CFG_ECC_ENABLE;
		return HAL_SPI_NAND_Set_Cfg(hal_nand_device, &cfg);
	}
	return 0;

}

/**
 * @brief HAL_SPI_NAND_Check_Ecc_Status
 * @param  status           ECC状态，直接从0xc0特性寄存器中读取，或者使用从HAL_SPI_NAND_Wait中获取的s参数
 * @param  corrected        NAND是否能够纠正错误
 * @param  ecc_error        NAND是否存在ECC错误
 */
static void HAL_SPI_NAND_Check_Ecc_Status(uint32_t status, uint32_t *corrected, uint32_t *ecc_error)
{
	unsigned int ecc_status = status & SPI_NAND_AS5F_ECC_MASK;

	*ecc_error = (ecc_status == SPI_NAND_AS5F_ECC_UNCORR);
	switch (ecc_status) {
	case SPI_NAND_AS5F_ECC_0_BIT:
		*corrected = 0;
		break;
	case SPI_NAND_AS5F_ECC_SHIFT:
		*corrected = 1;
		break;
	case SPI_NAND_AS5F_ECC_MAX_BIT:
		*corrected = 3;
		break;
	}
}

/**
 * @brief HAL_SPI_NAND_Wait 通过读取NAND的状态寄存器（0xc0），获取NAND是否处于BUSY状态，直到操作完成时退出
 * @param  status           状态寄存器读取的值，可以判断BUSY结束瞬间，进行的操作是否成功
 * @return int              需要配置超时退出时，使用负值标记超时退出，否则恒为0
 */
static int HAL_SPI_NAND_Wait(HAL_NAND_Device_t hal_nand_device, uint8_t *s)
{
	unsigned long long start = get_ticks();
	uint8_t status;
    uint32_t ret = -1;
	/* set timeout to 1 second */
	// int timeout = start + 100;
	// unsigned long ret = -ETIMEDOUT;//可以使用ticks比较的方法使能超时退出

	while (1) 
    {
		HAL_SPI_NAND_Read_Status(hal_nand_device, &status);
        LOG_D("wait status Read %02x", status);
		if ((status & STATUS_OIP_MASK) == STATUS_READY) {
			ret = 0;
			goto out;
		}
        else
        {
            LOG_D("busy");
        }
        mdelay(50);//delay 50ms
	}
out:
	if (s)
		*s = status;

	return ret;
}

/**
 * @brief HAL_SPI_NAND_Write_Enable 使能写操作，写数据之前必须使能该操作
 * @param  hal_nand_device  NAND设备
 * @return int 通信函数的返回值
 */
static int HAL_SPI_NAND_Write_Enable(HAL_NAND_Device_t hal_nand_device)
{
    return  HAL_SPI_NAND_xfer(hal_nand_device, SPINAND_CMD_WR_ENABLE, 0x00, NULL, NULL, 0, SPI_FIFO);
}

/**
 * @brief HAL_SPI_NAND_Write_Disable 停止写操作
 * @param  hal_nand_device  NAND设备
 * @return int 通信函数的返回值
 */
static int HAL_SPI_NAND_Write_Disable(HAL_NAND_Device_t hal_nand_device)
{
    return  HAL_SPI_NAND_xfer(hal_nand_device, SPINAND_CMD_WR_DISABLE, 0x00, NULL, NULL, 0, SPI_FIFO);
}

/**
 * @brief HAL_SPI_NAND_Read_Page_To_Cache 读取NAND数据到缓存区（该缓存时NAND自带的，一般和page的物理大小对齐，不需要消耗本机的heap）
 * @param  hal_nand_device  NAND设备
 * @param  page_addr        page 地址,假设一个block有64个page，那么page就是0~63（低六位），所属的block数就是后续的高位，发送固定为24个bit，不足的在低位补0（直接uint32表示即可）
 * @return int 通信函数的返回值 
 */
static int HAL_SPI_NAND_Read_Page_To_Cache(HAL_NAND_Device_t hal_nand_device, uint32_t page_addr)
{
    return HAL_SPI_NAND_xfer(hal_nand_device, SPINAND_CMD_PAGE_READ, page_addr, NULL, NULL, 0, SPI_FIFO);
}

/**
 * @brief HAL_SPI_NAND_Read_From_Cache 读取NAND缓存区数据
 * @param  hal_nand_device  NAND设备
 * @param  page_addr        page 地址,实际上没用
 * @param  column           列地址，即page内特定bit的地址，可以直接操作到bit
 * @param  len              数据长度
 * @param  din_buf          数据buffer
 * @return int通信函数的返回值 
 */
static int HAL_SPI_NAND_Read_From_Cache(HAL_NAND_Device_t hal_nand_device, uint32_t page_addr, uint32_t column, size_t len, uint8_t *din_buf)
{

    return HAL_SPI_NAND_xfer(hal_nand_device, SPINAND_CMD_READ_FROM_CACHE, column, din_buf, NULL, len, SPI_FIFO);
}

/**
 * @brief HAL_SPI_NAND_Program_Data_To_Cache 将数据写入缓存区
 * @param  hal_nand_device  NAND设备
 * @param  page_addr        page 地址,实际上没用
 * @param  column           列地址，即page内特定bit的地址，可以直接操作到bit
 * @param  len              数据长度
 * @param  dout_buf         数据buffer
 * @param  clr_cache        是否清空缓存区，1为清空；0为不清空，但是改写cache对应位置的数据
 * @return int通信函数的返回值 
 */
static int HAL_SPI_NAND_Program_Data_To_Cache(HAL_NAND_Device_t hal_nand_device, uint32_t page_addr, uint32_t column, size_t len, uint8_t *dout_buf, bool clr_cache)
{
    uint8_t cmd = 0;

    if (clr_cache) {
        cmd = SPINAND_CMD_PROG_LOAD;
    } else {
        cmd = SPINAND_CMD_PROG_LOAD_RDM_DATA;
    }


    return HAL_SPI_NAND_xfer(hal_nand_device, cmd, column, NULL, dout_buf, len, SPI_FIFO);
}

/**
 * @brief HAL_SPI_NAND_Program_Execute 写入缓存区数据到NAND
 * @param  hal_nand_device  NAND设备
 * @param  page_addr        page 地址
 * @return int 
 */
static int HAL_SPI_NAND_Program_Execute(HAL_NAND_Device_t hal_nand_device , uint32_t page_addr)
{
	return HAL_SPI_NAND_xfer(hal_nand_device, SPINAND_CMD_PROG_EXC, page_addr, NULL, NULL, 0, SPI_FIFO);
}

/**
 * @brief HAL_SPI_NAND_Erase_Block 擦除NAND的block
 * @param  hal_nand_device  NAND设备
 * @param  block_addr       block地址，和page地址格式一样的，只是处理时会忽略page地址，只按block操作
 * @return int 通信函数的返回值 
 */
static int HAL_SPI_NAND_Erase_Block(HAL_NAND_Device_t hal_nand_device, uint32_t block_addr)
{
    return HAL_SPI_NAND_xfer(hal_nand_device, SPINAND_CMD_BLK_ERASE, block_addr, NULL, NULL, 0, SPI_FIFO);
}

/**
 * @brief HAL_SPI_NAND_Internal_Data_Move 内部数据移动，将NAND的某页数据移动到另一个页 
 * @param  hal_nand_device  NAND设备
 * @param  page_src_addr    源数据的页地址
 * @param  page_dst_addr    移动的目标页地址
 * @param  offset           如果移动过程中需要改写数据，这就是改写的列地址，不改写就置空
 * @param  buf              数据buf
 * @param  len              数据长度
 * @return int 
 */
int HAL_SPI_NAND_Internal_Data_Move(HAL_NAND_Device_t hal_nand_device, uint32_t page_src_addr,  \
    uint32_t page_dst_addr, uint32_t offset, const uint8_t *buf, size_t len)
{
	uint8_t status;

	HAL_SPI_NAND_Read_Page_To_Cache(hal_nand_device, page_src_addr);
	if (HAL_SPI_NAND_Wait(hal_nand_device, &status)) 
    {
		return -ETIMEDOUT;
	}
	HAL_SPI_NAND_Write_Enable(hal_nand_device);
	if (buf != NULL)
    {
        HAL_SPI_NAND_Program_Data_To_Cache(hal_nand_device, page_dst_addr, offset, len, buf, 0);
    }

	HAL_SPI_NAND_Program_Execute(hal_nand_device, page_dst_addr);
	if (HAL_SPI_NAND_Wait(hal_nand_device, &status)) 
    {
		return -ETIMEDOUT;
	}
	if ((status & STATUS_P_FAIL_MASK) == STATUS_P_FAIL) 
    {
		return -EIO;
	}

	return 0;
}

/*测试函数，忽略*/
void hal_spi_test_oneline(void)
{
    char id[2] = {0};
    uint32_t page_addr_for_test = 0x100;
    uint32_t test_column = 0x100;
    uint8_t status = 0;
    uint8_t cfg = 0;
    // char dout_buff[4096] = {0};
    char din_buff[16] = {0};
    uint8_t lock_map = 0;
    HAL_SPI_NAND_Init(hal_nand_device);

    HAL_SPI_NAND_Read_ID(hal_nand_device, id);

    LOG_D("READ ID: %02X %02X", id[0], id[1]);
    HAL_SPI_NAND_Get_Lock_Block(hal_nand_device, &lock_map);
    LOG_D("DEFAULT lock %02X", lock_map);
    lock_map = 0x00;
    HAL_SPI_NAND_Lock_Block(hal_nand_device, &lock_map);
    lock_map = 0;
    HAL_SPI_NAND_Get_Lock_Block(hal_nand_device, &lock_map);
    LOG_D("Now lock %02X", lock_map);


    status = 0;

    // HAL_SPI_NAND_Get_Cfg(hal_nand_device, &cfg);

    // cfg = cfg | 0x01;
    // HAL_SPI_NAND_Set_Cfg(hal_nand_device, &cfg);
    // cfg=0;
    // HAL_SPI_NAND_Get_Cfg(hal_nand_device, &cfg);
    LOG_D("cfg %02X", cfg);

    //memcpy(dout_buff, "ABCDEFG", 8);


    
    HAL_SPI_NAND_Write_Enable(hal_nand_device);
    HAL_SPI_NAND_Read_Status(hal_nand_device,&status);

    LOG_D("status before erase %02X", status);
    HAL_SPI_NAND_Erase_Block(hal_nand_device, page_addr_for_test);//擦除第10块

    HAL_SPI_NAND_Wait(hal_nand_device, &status);

    // LOG_D("Erase END");

    // HAL_SPI_NAND_Read_Page_To_Cache(hal_nand_device, page_addr_for_test);

    // HAL_SPI_NAND_Wait(hal_nand_device, &status);

    // HAL_SPI_NAND_Read_From_Cache(hal_nand_device, page_addr_for_test, test_column, 16, din_buff);

    // LOG_D("Read END 1 %02X %02X %02X %02X %02X %02X %02X %02X",\
    // din_buff[0], din_buff[1], din_buff[2], din_buff[3], din_buff[4], din_buff[5], din_buff[6], din_buff[7]);

    HAL_SPI_NAND_Write_Enable(hal_nand_device);

    HAL_SPI_NAND_Read_Status(hal_nand_device,&status);
    LOG_D("status %02X", status);
    status = 0;


    HAL_SPI_NAND_Program_Data_To_Cache(hal_nand_device, page_addr_for_test, test_column, 8, (uint8_t *)"ABCDEFG", true);

    HAL_SPI_NAND_Read_From_Cache(hal_nand_device, page_addr_for_test, test_column, 8, din_buff);

    LOG_D("Read END 1 %02X %02X %02X %02X %02X %02X %02X %02X \r\n str %s",\
    din_buff[0], din_buff[1], din_buff[2], din_buff[3], din_buff[4], din_buff[5], din_buff[6], din_buff[7], din_buff);

    HAL_SPI_NAND_Program_Execute(hal_nand_device, page_addr_for_test);

    HAL_SPI_NAND_Wait(hal_nand_device, &status);

    LOG_D("Program END");

    HAL_SPI_NAND_Read_Page_To_Cache(hal_nand_device, page_addr_for_test);

    HAL_SPI_NAND_Wait(hal_nand_device, &status);

    //memset(din_buff, 0x00, sizeof(din_buff));

    HAL_SPI_NAND_Read_From_Cache(hal_nand_device, page_addr_for_test, test_column, 8, din_buff);

    LOG_D("Read END 2 %02X %02X %02X %02X %02X %02X %02X %02X \r\n str %s",\
    din_buff[0], din_buff[1], din_buff[2], din_buff[3], din_buff[4], din_buff[5], din_buff[6], din_buff[7], din_buff);

}

// MSH_CMD_EXPORT(hal_spi_test_oneline, HAL SPI TEST ONELINE);